package com.nc.user.config.shiro;


import com.alibaba.fastjson.JSON;
import com.nc.user.controller.AccessTokenController;
import com.nc.user.model.entity.FrmRequestLog;
import com.nc.user.model.entity.FrmUsers;
import com.nc.user.service.LogService;
import com.nc.user.service.RedisService;
import com.nc.user.tool.tool.WebUtils;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;


import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/*
 * 认证过滤器
 *
 * */

public class AuthFilter extends FormAuthenticationFilter {
    /**
     * 用户类型
     */
    private Class<? extends FrmUsers> userClass;
    /**
     * "重定向URL"参数名称
     */
    private static final String REDIRECT_URL_PARAMETER_NAME = "redirectUrl";

    public LogService logService;
    public RedisService redisService;

    public AuthFilter(LogService logService, RedisService redisService) {
        this.logService = logService;
        this.redisService = redisService;
    }

    /**
     * 创建令牌
     *
     * @param servletRequest  ServletRequest
     * @param servletResponse ServletResponse
     * @return 令牌
     */

    @Override
    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) {
        String username = getUsername(servletRequest);
        String password = getPassword(servletRequest);
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        if (WebUtils.isAjaxRequest(request)) {
            JSONObject ret = JSONObject.fromObject(SecurityUtils.getSubject().getSession().getAttribute("ret"));
            username = ret.getString("username");
            password = ret.getString("password");
        }

        boolean rememberMe = isRememberMe(servletRequest);
        String host = getHost(servletRequest);

        return new UserAuthenticationToken(FrmUsers.class, username, password, rememberMe, host);
    }

    /**
     * 是否允许访问
     *
     * @param servletRequest  ServletRequest
     * @param servletResponse ServletResponse
     * @param mappedValue     映射值
     * @return 是否允许访问
     */


    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) {
        HttpServletRequest request = (HttpServletRequest) servletRequest;


        Subject subject = getSubject(servletRequest, servletResponse);
        Object principal = subject != null ? subject.getPrincipal() : null;
        if (principal == null || !FrmUsers.class.isAssignableFrom(principal.getClass())) {
            return false;
        }

        if (request.getServletPath().indexOf("login") >= 0 && request.getSession().getAttribute("Users") != null) {
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            return false;
        }
        return super.isAccessAllowed(servletRequest, servletResponse, mappedValue);
    }

    /**
     * 拒绝访问处理
     *
     * @param servletRequest  ServletRequest
     * @param servletResponse ServletResponse
     * @return 是否继续处理
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        JSONObject jsonObject = new JSONObject();
        if (WebUtils.isAjaxRequest(request)) {
            InputStream inputStream = null;
            BufferedReader bufferedReader = null;
            StringBuilder stringBuilder = new StringBuilder();
            try {
                inputStream = request.getInputStream();
                if (inputStream != null) {
                    bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                    char[] charBuffer = new char[128];
                    int bytesRead = -1;
                    while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                        stringBuilder.append(charBuffer, 0, bytesRead);
                    }
                    jsonObject = StringUtils.isNotBlank(stringBuilder.toString()) ? JSONObject.fromObject(stringBuilder.toString()) : new JSONObject();
                    SecurityUtils.getSubject().getSession().setAttribute("ret", jsonObject.toString());
                }
            } catch (IOException e) {

            }
        }
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                String verCode = redisService.get(request.getRemoteHost());
                String captcha = jsonObject.getString("captcha");
                if (verCode.equals(captcha)) {
                    return executeLogin(request, response);
                } else {
                    response.getWriter().println("{\"code\":\"402\"}");
                }
            } else {
                return true;
            }
        }
        if (WebUtils.isAjaxRequest(request)) {
            response.getWriter().println("{\"code\":\"401\"}");
        } else {
            response.sendRedirect(request.getContextPath() + getLoginUrl());
        }
        return false;
    }

    /**
     * 登录成功处理
     *
     * @param authenticationToken 令牌
     * @param subject             Subject
     * @param servletRequest      ServletRequest
     * @param servletResponse     ServletResponse
     * @return 是否继续处理
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken authenticationToken, Subject subject, ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        logService.LoginLog(request, FrmRequestLog.Status.YES);
        FrmUsers fspuser = (FrmUsers) SecurityUtils.getSubject().getSession().getAttribute("Users");
        redisService.set(subject.getSession().getId().toString(),JSON.toJSONString(fspuser));
        if (WebUtils.isAjaxRequest(request)) {
            response.getWriter().println("{\"code\":\"200\",\"token\":\"" + subject.getSession().getId() + "\",\"userInfo\":" + JSON.toJSONString(fspuser) + "}");
            response.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.onLoginSuccess(authenticationToken, subject, servletRequest, servletResponse);
    }

    /**
     * 登录失败处理
     *
     * @param authenticationToken     令牌
     * @param authenticationException 认证异常
     * @param servletRequest          ServletRequest
     * @param servletResponse         ServletResponse
     * @return 是否继续处理
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken authenticationToken, AuthenticationException authenticationException, ServletRequest servletRequest, ServletResponse servletResponse) {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if (WebUtils.isAjaxRequest(request)) {
            try {
                response.getWriter().println("{\"code\":\"400\",\"msg\":\"账号或密码不正确\",\"data\":\"\",\"num\":\"\"}");
            } catch (IOException e) {
                e.printStackTrace();
            }
            return false;
        }
        logService.LoginLog(request, FrmRequestLog.Status.NO);
        return super.onLoginFailure(authenticationToken, authenticationException, servletRequest, servletResponse);
    }

    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        setHeader(httpRequest, httpResponse);
        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            return false;
        }
        return super.preHandle(request, response);
    }

    private void setHeader(HttpServletRequest request, HttpServletResponse response) {
        //跨域的header设置
        response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Methods", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,token");
        response.setHeader("X-Requested-With", request.getHeader("X-Requested-With"));
        //防止乱码，适用于传输JSON数据
        //Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild
        response.setHeader("Content-Type", "application/json;charset=UTF-8");
        response.setStatus(HttpStatus.OK.value());
    }

    public Class<? extends FrmUsers> getUserClass() {
        return userClass;
    }

    public void setUserClass(Class<? extends FrmUsers> userClass) {
        this.userClass = userClass;
    }

    public static String getRedirectUrlParameterName() {
        return REDIRECT_URL_PARAMETER_NAME;
    }
}
